﻿using ArtMath.AuxiliaryTool;
using ArtMath.CommonType;
using ArtMath.Control;
using ArtMath.Core.Helper;
using ArtMath.Flag;
using ArtMath.Resource;
using ArtMath.Setting;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;

namespace ArtMath.Window
{
    public partial class SettingsWindow : Form
    {
        public class InitializationData
        {
            public List<NamedField> Switches = new List<NamedField>(), Numbers = new List<NamedField>(),
                Enumerations = new List<NamedField>(), Colors = new List<NamedField>(), Fonts = new List<NamedField>();
            public List<ListViewItem> FontSizeItems = new List<ListViewItem>(), FontFamilyItems = new List<ListViewItem>();
            public FieldInfo LangField;
            public Dictionary<NamedField, Dictionary<object, string>> EnumConverters = new Dictionary<NamedField, Dictionary<object, string>>();
        }
        public class NamedField
        {
            public FieldInfo Field { get; }
            public string Name { get; }
            public NamedField(FieldInfo fi)
            {
                Field = fi;
                Name = (Field.FieldType == typeof(bool) && Field.Name.Contains("UseFormat") ? Resources.Translate("UseFormat") : string.Empty)
                    + Settings.GetFieldDisplayName(Field);
            }
            public override string ToString()
            {
                return Name;
            }
        }
        class NamedEnumValue
        {
            public object EnumValue { get; }
            public string EnumName { get; }
            public NamedEnumValue(object value, string name)
            {
                EnumValue = value;
                EnumName = name;
            }
            public override string ToString()
            {
                return EnumName;
            }
        }
        bool colorCS, colorMWW, fontCS, fontMWW,
            setFromPicker = true,
            doReset, submitModify = true,
            syncFont = true, syncFontSize = true;
        float selectedH, selectedS = 1, selectedV = 1;
        int selectedA = 255;
        Dictionary<System.Windows.Forms.Control, string> numOldValues = new Dictionary<System.Windows.Forms.Control, string>();
        Dictionary<FieldInfo, object> newValues = new Dictionary<FieldInfo, object>();
        const int transparencyBlockSize = 10, colorIndex = 0, fontIndex = 1;
        public static InitializationData InitData;
        void Switch_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox chb = sender as CheckBox;
            FieldInfo fi = chb.Tag as FieldInfo;
            if (newValues.ContainsKey(fi))
            {
                newValues[fi] = chb.Checked;
            }
            else
            {
                newValues.Add(fi, chb.Checked);
            }
            doReset = false;
        }
        public SettingsWindow()
        {
            InitializeComponent();
            tbSearchColor.PlaceHolderText = Resources.JoinLocalizedString("Search", "Color");
            tbSearchFont.PlaceHolderText = Resources.JoinLocalizedString("Search", "Font");
            splitContainer1.Panel2.Enabled = false;
            lbColorItems.Items.AddRange(InitData.Colors.ToArray());
            lbFontItems.Items.AddRange(InitData.Fonts.ToArray());
            lvFonts.Items.AddRange(InitData.FontFamilyItems.ToArray());
            lvFontSize.Items.AddRange(InitData.FontSizeItems.ToArray());
            TableLayoutPanel switchLayout = new TableLayoutPanel()
            {
                RowCount = InitData.Switches.Count,
                ColumnCount = 1,
                Dock = DockStyle.Fill,
                AutoScroll = true,
            };
            tabPage3.Controls.Add(switchLayout);
            switchLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
            for (int i = 0; i < InitData.Switches.Count; i++)
            {
                CheckBox cb = new CheckBox()
                {
                    Text = InitData.Switches[i].Name,
                    Checked = (bool)InitData.Switches[i].Field.GetValue(Settings.Instance),
                    Tag = InitData.Switches[i].Field,
                    AutoSize = true,
                };
                cb.CheckedChanged += Switch_CheckedChanged;
                switchLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 30));
                switchLayout.Controls.Add(cb, 0, i);
            }
            TableLayoutPanel numLayout = new TableLayoutPanel()
            {
                RowCount = InitData.Numbers.Count,
                ColumnCount = 2,
                Dock = DockStyle.Fill,
                AutoScroll = true,
            };
            tabPage4.Controls.Add(numLayout);
            numLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70));
            numLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30));
            for (int i = 0; i < InitData.Numbers.Count; i++)
            {
                object[] attr = InitData.Numbers[i].Field.GetCustomAttributes(typeof(Settings.UnitAttribute), true);
                string unit;
                if (attr?.Length > 0)
                {
                    unit = "(" + (attr[0] as Settings.UnitAttribute).Unit + ")";
                }
                else
                {
                    unit = string.Empty;
                }
                Label lbl = new Label()
                {
                    Text = InitData.Numbers[i].Name + unit,
                    AutoSize = true,
                    AutoEllipsis = true,
                };
                numLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 30));
                numLayout.Controls.Add(lbl, 0, i);
                if (InitData.Numbers[i].Field.FieldType == typeof(int))
                {
                    NumericUpDown nud = new NumericUpDown()
                    {
                        Tag = InitData.Numbers[i].Field,
                        Dock = DockStyle.Fill,
                    };
                    attr = InitData.Numbers[i].Field.GetCustomAttributes(typeof(Settings.RangeAttribute), true);
                    if (attr?.Length > 0)
                    {
                        Settings.RangeAttribute range = attr[0] as Settings.RangeAttribute;
                        nud.Minimum = range.Minimum;
                        nud.Maximum = range.Maximum;
                    }
                    nud.Value = decimal.Parse(InitData.Numbers[i].Field.GetValue(Settings.Instance).ToString());
                    nud.ValueChanged += NumericUpDown_ValueChanged;
                    numLayout.Controls.Add(nud, 1, i);
                }
                else
                {
                    TextBox tb = new TextBox()
                    {
                        Text = InitData.Numbers[i].Field.GetValue(Settings.Instance).ToString(),
                        Tag = InitData.Numbers[i].Field,
                        Dock = DockStyle.Fill,
                    };
                    tb.GotFocus += TextBox_GotFocus;
                    tb.Validated += TextBox_Validated;
                    numLayout.Controls.Add(tb, 1, i);
                }
            }
            TableLayoutPanel enumLayout = new TableLayoutPanel()
            {
                RowCount = InitData.Enumerations.Count + 1,
                ColumnCount = 2,
                Dock = DockStyle.Fill,
                AutoScroll = true,
            };
            tabPage5.Controls.Add(enumLayout);
            enumLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30));
            enumLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70));
            for (int i = -1; i < InitData.Enumerations.Count; i++)
            {
                Label lbl = new Label()
                {
                    Text = i == -1 ? Resources.Translate("Language") : InitData.Enumerations[i].Name,
                    AutoSize = true,
                    AutoEllipsis = true,
                };
                enumLayout.RowStyles.Add(new RowStyle(SizeType.Absolute, 30));
                enumLayout.Controls.Add(lbl, 0, i == -1 ? enumLayout.RowCount - 1 : i);
                ComboBox cb = new ComboBox()
                {
                    DropDownStyle = ComboBoxStyle.DropDownList,
                    Tag = i == -1 ? InitData.LangField : InitData.Enumerations[i].Field,
                    Dock = DockStyle.Fill,
                };
                if (i == -1)
                {
                    cb.Items.Add(new NamedEnumValue("zh-CN", "简体中文"));
                    cb.Items.Add(new NamedEnumValue("en-US", "English"));
                }
                else
                {
                    foreach (KeyValuePair<object, string> item in InitData.EnumConverters[InitData.Enumerations[i]])
                    {
                        cb.Items.Add(new NamedEnumValue(item.Key, item.Value));
                    }
                }
                for (int j = 0; j < cb.Items.Count; j++)
                {
                    if ((cb.Items[j] as NamedEnumValue).EnumValue.Equals((cb.Tag as FieldInfo).GetValue(Settings.Instance)))
                    {
                        cb.SelectedIndex = j;
                        break;
                    }
                }
                cb.SelectedIndexChanged += Enum_SelectedIndexChanged;
                enumLayout.Controls.Add(cb, 1, i == -1 ? enumLayout.RowCount - 1 : i);
            }
            tbH.GotFocus += TextBox_GotFocus;
            tbH.Validated += (object sender, EventArgs e) =>
            {
                HSV_Validated(tbH, (double value) =>
                {
                    bool res = value >= 0 && value < 360;
                    if (res)
                    {
                        selectedH = (float)value;
                    }
                    return res;
                }, Resources.Translate("HOutofRange"));
            };
            tbS.GotFocus += TextBox_GotFocus;
            tbS.Validated += (object sender, EventArgs e) =>
            {
                HSV_Validated(tbS, (double value) =>
                {
                    bool res = value >= 0 && value <= 100;
                    if (res)
                    {
                        selectedS = (float)value / 100;
                    }
                    return res;
                }, Resources.Translate("SOutofRange"));
            };
            tbV.GotFocus += TextBox_GotFocus;
            tbV.Validated += (object sender, EventArgs e) =>
            {
                HSV_Validated(tbV, (double value) =>
                {
                    bool res = value >= 0 && value <= 100;
                    if (res)
                    {
                        selectedV = (float)value / 100;
                    }
                    return res;
                }, Resources.Translate("VOutofRange"));
            };
            tbHex.GotFocus += TextBox_GotFocus;
        }
        void Enum_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox box = sender as ComboBox;
            FieldInfo fi = box.Tag as FieldInfo;
            object value = (box.SelectedItem as NamedEnumValue).EnumValue;
            if (newValues.ContainsKey(fi))
            {
                newValues[fi] = value;
            }
            else
            {
                newValues.Add(fi, value);
            }
            doReset = false;
        }
        void NumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            NumericUpDown nud = sender as NumericUpDown;
            FieldInfo fi = nud.Tag as FieldInfo;
            if (newValues.ContainsKey(fi))
            {
                newValues[fi] = (int)nud.Value;
            }
            else
            {
                newValues.Add(fi, (int)nud.Value);
            }
            doReset = false;
        }
        void TextBox_Validated(object sender, EventArgs e)
        {
            TextBox txt = sender as TextBox;
            string oldVal = numOldValues[txt];
            numOldValues.Remove(txt);
            if (txt.Text == oldVal) return;
            if (!Calculation.Calculate(txt.Text, out double value))
            {
                MessageBox.Show(string.Format(Resources.Translate("InvalidExpression"), txt.Text), Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                txt.Text = oldVal;
                return;
            }
            FieldInfo fi = txt.Tag as FieldInfo;
            object newValue;
            if (fi.FieldType == typeof(string))
            {
                newValue = txt.Text;
            }
            else
            {
                newValue = Convert.ChangeType(value, fi.FieldType);
            }
            if (newValues.ContainsKey(fi))
            {
                newValues[fi] = newValue;
            }
            else
            {
                newValues.Add(fi, newValue);
            }
            doReset = false;
        }
        void TextBox_GotFocus(object sender, EventArgs e)//备份旧值
        {
            TextBox txt = sender as TextBox;
            if (numOldValues.ContainsKey(txt))
            {
                numOldValues[txt] = txt.Text;
            }
            else
            {
                numOldValues.Add(txt, txt.Text);
            }
        }
        Color PickedColor
        {
            get { return Color.FromArgb(selectedA, PickedBaseColor); }
        }
        Color PickedBaseColor
        {
            get { return Utility.HSVToRGB(selectedH, selectedS, selectedV); }
        }
        Color InputColor
        {
            get { return Color.FromArgb((int)nudA.Value, InputBaseColor); }
        }
        Color InputBaseColor
        {
            get { return Color.FromArgb((int)nudR.Value, (int)nudG.Value, (int)nudB.Value); }
        }
        Font PickedFont
        {
            get
            {
                FontStyle fs = FontStyle.Regular;
                if (cbBold.Checked)
                {
                    fs |= FontStyle.Bold;
                }
                if (cbItalic.Checked)
                {
                    fs |= FontStyle.Italic;
                }
                if (cbUnderline.Checked)
                {
                    fs |= FontStyle.Underline;
                }
                if (cbStrikeout.Checked)
                {
                    fs |= FontStyle.Strikeout;
                }
                string fontFamily;
                if (lvFonts.SelectedItems.Count == 1)
                {
                    fontFamily = lvFonts.SelectedItems[0].Text;
                }
                else
                {
                    fontFamily = SystemFonts.DefaultFont.FontFamily.Name;
                }
                float size;
                if (lvFontSize.SelectedItems.Count == 1)
                {
                    size = Convert.ToSingle(lvFontSize.SelectedItems[0].Tag);
                }
                else
                {
                    size = 9;
                }
                return new Font(fontFamily, size, fs);
            }
        }
        private void btnOK_Click(object sender, EventArgs e)
        {
            UIColorTheme oldTheme = Settings.Instance.ColorTheme;
            if (doReset)
            {
                Settings.Instance.Reset();
            }
            else
            {
                foreach (KeyValuePair<FieldInfo, object> item in newValues)
                {
                    item.Key.SetValue(Settings.Instance, item.Value);
                }
            }
            if (Settings.Instance.AutoSaveInterval == 0)
            {
                if (Program.WinDocuments.AutoSave.Enabled)
                {
                    Program.WinDocuments.AutoSave.Stop();
                }
            }
            else
            {
                Program.WinDocuments.AutoSave.Interval = Settings.Instance.AutoSaveInterval * 1000;
                if (!Program.WinDocuments.AutoSave.Enabled)
                {
                    Program.WinDocuments.AutoSave.Start();
                }
            }
            if (Settings.Instance.FlashInterval == 0)
            {
                if (Program.WinDocuments.ChangeInvertionState.Enabled)
                {
                    Program.WinDocuments.ChangeInvertionState.Stop();
                }
            }
            else
            {
                Program.WinDocuments.ChangeInvertionState.Interval = Settings.Instance.FlashInterval;
                if (!Program.WinDocuments.ChangeInvertionState.Enabled && Program.WinDocuments.HasSelectedItems)
                {
                    Program.WinDocuments.ChangeInvertionState.Start();
                }
            }
            foreach (ListViewItem item in Program.WinDocuments.DocumentList.Items)
            {
                Document doc = item.Tag as Document;
                if (doc == null) continue;
                foreach (SketchPad pad in doc.Pads)
                {
                    foreach (LayerItem litem in pad.Layer.Items)
                    {
                        litem.LayerItemHeight = Settings.Instance.LayerItemHeight;
                    }
                }
            }
            Program.WinMain.LinkedPad?.OperateRing((Ring rng) => { rng.Radius = Settings.Instance.RingRadius; });
            if (Settings.Instance.ColorTheme != oldTheme)
            {
                MessageBox.Show(Resources.Translate("ThemeRestart"), Resources.Translate("Prompt"), MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            DialogResult = DialogResult.OK;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
        }

        private void btnSetDefault_Click(object sender, EventArgs e)
        {
            doReset = true;
        }

        private void btnImportSettings_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog()
            {
                Title = Resources.Translate("Open"),
                Filter = Resources.JoinLocalizedString("Settings", "File") + "(*.cfg)|*.cfg",
                InitialDirectory = Flags.DesktopPath,
            };
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                IFormatter fmt = new BinaryFormatter();
                try
                {
                    Stream stream = new FileStream(ofd.FileName, FileMode.Open, FileAccess.Read, FileShare.None);
                    Settings.Instance = fmt.Deserialize(stream) as Settings;
                    stream.Close();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                MessageBox.Show(Resources.JoinLocalizedString("Import", "Complete"), Resources.Translate("Prompt"), MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void btnExportSettings_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog()
            {
                Title = Resources.Translate("SaveAs"),
                Filter = Resources.JoinLocalizedString("Settings", "File") + "(*.cfg)|*.cfg",
                InitialDirectory = Flags.DesktopPath,
            };
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                if (Settings.Instance.SaveAs(sfd.FileName))
                {
                    MessageBox.Show(Resources.JoinLocalizedString("Export", "Complete"), Resources.Translate("Prompt"), MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        private void btnColorSearchSettings_Click(object sender, EventArgs e)
        {
            contextMenuStrip1.Show(btnColorSearchSettings, new Point(0, btnColorSearchSettings.Height));
        }
        void searchSettingItem(ListBox listBox, string searchText)
        {
            if (searchText.Length > 0)
            {
                for (int i = 0; i < listBox.Items.Count; i++)
                {
                    string subitemText = listBox.Items[i].ToString();
                    if (!区分大小写ToolStripMenuItem.Checked)
                    {
                        subitemText = subitemText.ToLower();
                        searchText = searchText.ToLower();
                    }
                    if (subitemText.Contains(searchText))
                    {
                        if (全字匹配ToolStripMenuItem.Checked)
                        {
                            listBox.SetSelected(i, subitemText.Length == searchText.Length);
                        }
                        else
                        {
                            listBox.SetSelected(i, true);
                        }
                    }
                    else
                    {
                        listBox.SetSelected(i, false);
                    }
                }
            }
            else
            {
                for (int i = 0; i < listBox.Items.Count; i++)
                {
                    listBox.SetSelected(i, false);
                }
            }
        }

        private void tbSearchColor_TextChanged(object sender, EventArgs e)
        {
            searchSettingItem(lbColorItems, tbSearchColor.Text);
        }

        private void lbColorItems_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lbColorItems.SelectedItems.Count > 0)
            {
                splitContainer1.Panel2.Enabled = true;
                bool transEnabled = true;
                foreach (NamedField item in lbColorItems.SelectedItems)
                {
                    object[] attr = item.Field.GetCustomAttributes(typeof(Settings.NonTransparentAttribute), true);
                    if (attr?.Length > 0)
                    {
                        transEnabled = false;
                        break;
                    }
                }
                pbTransparency.Enabled = label1.Enabled = nudA.Enabled = transEnabled;
                if (!nudA.Enabled)
                {
                    nudA.Value = 255;
                }
                if (lbColorItems.SelectedItems.Count == 1)
                {
                    FieldInfo fi = (lbColorItems.SelectedItems[0] as NamedField).Field;
                    Color cl = (Color)fi.GetValue(Settings.Instance);
                    submitModify = false;
                    nudA.Value = cl.A;
                    nudR.Value = cl.R;
                    nudG.Value = cl.G;
                    nudB.Value = cl.B;
                    refreshHSVPicker();
                    refreshHexagon();
                    submitModify = true;
                }
            }
            else
            {
                splitContainer1.Panel2.Enabled = false;
            }
        }
        void submitColor()
        {
            foreach (NamedField item in lbColorItems.SelectedItems)
            {
                if (newValues.ContainsKey(item.Field))
                {
                    newValues[item.Field] = InputColor;
                }
                else
                {
                    newValues.Add(item.Field, InputColor);
                }
            }
            doReset = false;
        }
        private void pbHue_Paint(object sender, PaintEventArgs e)
        {
            Graphics gra = e.Graphics;
            float width = pbHue.ClientSize.Width / 360f;
            for (int i = 0; i < 360; i++)
            {
                Color cl = Utility.HSVToRGB(i, 1, 1);
                if (!pbHue.Enabled)
                {
                    cl = cl.GetGrayColor();
                }
                gra.FillRectangle(new SolidBrush(cl), width * i, 0, width, pbHue.ClientSize.Height);
            }
        }

        private void lbFontItems_SelectedIndexChanged(object sender, EventArgs e)
        {
            splitContainer2.Panel2.Enabled = lbFontItems.SelectedItems.Count > 0;
            if (lbFontItems.SelectedItems.Count == 1)
            {
                submitModify = false;
                FieldInfo fi = (lbFontItems.SelectedItems[0] as NamedField).Field;
                Font ft = fi.GetValue(Settings.Instance) as Font;
                responseFont(ft);
                submitModify = true;
            }
        }
        void responseFont(Font ft)
        {
            ListViewItem item = lvFonts.FindItemWithText(ft.FontFamily.Name);
            if (item != null)
            {
                item.Selected = true;
            }
            for (int i = 0; i < lvFontSize.Items.Count; i++)
            {
                if (Convert.ToSingle(lvFontSize.Items[i].Tag) == ft.Size)
                {
                    lvFontSize.Items[i].Selected = true;
                    break;
                }
            }
            cbBold.Checked = ft.Bold;
            cbItalic.Checked = ft.Italic;
            cbUnderline.Checked = ft.Underline;
            cbStrikeout.Checked = ft.Strikeout;
        }
        private void pbHue_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            pickHue(e);
        }

        private void pbColorPicker_Paint(object sender, PaintEventArgs e)
        {
            Graphics gra = e.Graphics;
            float width = pbColorPicker.ClientSize.Width / 101f, height = pbColorPicker.ClientSize.Height / 101f;
            for (int x = 0; x <= 100; x++)
            {
                for (int y = 0; y <= 100; y++)
                {
                    Color cl = Utility.HSVToRGB(setFromPicker ? selectedH : InputBaseColor.GetHue(), x / 100f, y / 100f);
                    if (!pbColorPicker.Enabled)
                    {
                        cl = cl.GetGrayColor();
                    }
                    gra.FillRectangle(new SolidBrush(cl), 
                        x * width, (100 - y) * height, width, height);
                }
            }
        }

        private void pbTransparency_Paint(object sender, PaintEventArgs e)
        {
            Graphics gra = e.Graphics;
            //画网格
            Point loc = new Point(0, 0);
            bool firstWhite = true;
            while (pbTransparency.ClientRectangle.Contains(loc))
            {
                bool currentWhite = firstWhite;
                while (pbTransparency.ClientRectangle.Contains(loc))
                {
                    Color cl = currentWhite ? Color.White : Color.FromArgb(204, 204, 204);
                    if (!pbTransparency.Enabled)
                    {
                        cl = cl.GetGrayColor();
                    }
                    gra.FillRectangle(new SolidBrush(cl), loc.X, loc.Y, transparencyBlockSize, transparencyBlockSize);
                    loc.X += transparencyBlockSize;
                    currentWhite = !currentWhite;
                }
                loc.X = 0;
                loc.Y += transparencyBlockSize;
                firstWhite = !firstWhite;
            }
            //画透明色
            Color selected = setFromPicker ? PickedBaseColor : InputBaseColor;
            selected = pbTransparency.Enabled ? selected : selected.GetGrayColor();
            float height = pbTransparency.ClientSize.Height / 256f;
            for (int i = 0; i <= 255; i++)
            {
                gra.FillRectangle(new SolidBrush(Color.FromArgb(255 - i, selected)), 0, i * height, pbTransparency.ClientSize.Width, height);
            }
        }

        private void SettingsWindow_SizeChanged(object sender, EventArgs e)
        {
            switch (tabControl1.SelectedIndex)
            {
                case 0:
                    colorLayout();
                    break;
            }
        }

        private void SettingsWindow_Load(object sender, EventArgs e)
        {
            SettingsWindow_SizeChanged(sender, e);
        }
        void colorLayout()
        {
            pbHue.Left = pbHue.Top = pbColorPicker.Left = 10;
            pbHue.Width = pbHue.Parent.Width - 20;
            pbHue.Height = (int)(pbHue.Parent.Height * 0.2);
            pbColorPicker.Top = pbTransparency.Top = gbColorValue.Top = pbHue.Bottom + 10;
            pbColorPicker.Width = pbColorPicker.Height = pbTransparency.Height = pbColorPicker.Parent.ClientSize.Height - pbColorPicker.Top - 10;
            gbColorValue.Height = pbTransparency.Height - btnCDset.Height - 6;
            pbTransparency.Left = pbColorPicker.Right + 10;
            pbTransparency.Width = (int)(pbTransparency.Parent.Width * 0.1);
            gbColorValue.Left = btnCDset.Left = pbTransparency.Right + 10;
            gbColorValue.Width = btnCDset.Width = pbHue.Right - gbColorValue.Left;
            btnCDset.Top = pbTransparency.Bottom - btnCDset.Height;
            panel4.Left = (panel4.Parent.Width - panel4.Width) >> 1;
            panel4.Top = (panel4.Parent.Height - panel4.Height) >> 1;
        }
        private void pbColorPicker_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            pickSV(e);
        }
        void pickSV(System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                setFromPicker = true;
                if (pbColorPicker.ClientRectangle.Contains(e.Location))
                {
                    selectedS = (float)e.X / pbColorPicker.ClientSize.Width;
                    selectedV = 1 - (float)e.Y / pbColorPicker.ClientSize.Height;
                }
                else
                {
                    if (e.X < 0)
                    {
                        selectedS = 0;
                    }
                    else if (e.X > pbColorPicker.ClientSize.Width)
                    {
                        selectedS = 1;
                    }
                    if (e.Y < 0)
                    {
                        selectedV = 1;
                    }
                    else if (e.Y > pbColorPicker.ClientSize.Height)
                    {
                        selectedV = 0;
                    }
                }
                pbTransparency.Invalidate();
                tbS.Text = (selectedS * 100).ToString();
                tbV.Text = (selectedV * 100).ToString();
                refreshRGBValue();
                refreshHexagon();
                submitColor();
            }
        }

        private void pbColorPicker_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            pickSV(e);
        }

        private void pbTransparency_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            pickA(e);
        }
        void pickA(System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                setFromPicker = true;
                if (pbTransparency.ClientRectangle.Contains(e.Location))
                {
                    selectedA = (int)((1 - (float)e.Y / pbTransparency.ClientSize.Height) * 255);
                }
                else if (e.Y < 0)
                {
                    selectedA = 255;
                }
                else if (e.Y > pbTransparency.ClientSize.Height)
                {
                    selectedA = 0;
                }
                nudA.Value = selectedA;
                refreshHexagon();
                submitColor();
            }
        }

        private void pbTransparency_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            pickA(e);
        }

        private void nudA_ValueChanged(object sender, EventArgs e)
        {
            if (!submitModify) return;
            setFromPicker = false;
            selectedA = (int)nudA.Value;
            refreshHexagon();
            submitColor();
        }
        private void tbSearchFont_TextChanged(object sender, EventArgs e)
        {
            searchSettingItem(lbFontItems, tbSearchFont.Text);
        }

        private void btnFontSearchSettings_Click(object sender, EventArgs e)
        {
            contextMenuStrip1.Show(btnFontSearchSettings, new Point(0, btnFontSearchSettings.Height));
        }

        private void lvFonts_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (syncFont && lvFonts.SelectedItems.Count == 1)
            {
                tbFont.Text = lvFonts.SelectedItems[0].Text;
                showFont();
            }
        }

        private void lvFontSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (syncFontSize && lvFontSize.SelectedItems.Count == 1)
            {
                tbFontSize.Text = lvFontSize.SelectedItems[0].Text;
                showFont();
            }
        }
        void showFont()
        {
            tbPreview.Font = PickedFont;
        }

        private void cbBold_CheckedChanged(object sender, EventArgs e)
        {
            showFont();
        }

        private void cbUnderline_CheckedChanged(object sender, EventArgs e)
        {
            showFont();
        }

        private void cbItalic_CheckedChanged(object sender, EventArgs e)
        {
            showFont();
        }

        private void cbStrikeout_CheckedChanged(object sender, EventArgs e)
        {
            showFont();
        }

        private void btnFDset_Click(object sender, EventArgs e)
        {
            if (fontDialog1.ShowDialog() == DialogResult.OK)
            {
                tbPreview.Font = fontDialog1.Font;
                responseFont(fontDialog1.Font);
            }
        }

        private void btnCDset_Click(object sender, EventArgs e)
        {
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {

            }
        }

        private void nudG_ValueChanged(object sender, EventArgs e)
        {
            if (!submitModify) return;
            setFromPicker = false;
            refreshHSVPicker();
            refreshHexagon();
            submitColor();
        }

        private void nudB_ValueChanged(object sender, EventArgs e)
        {
            if (!submitModify) return;
            setFromPicker = false;
            refreshHSVPicker();
            refreshHexagon();
            submitColor();
        }

        private void tbPreview_FontChanged(object sender, EventArgs e)
        {
            if (!Visible || !submitModify) return;
            foreach (NamedField item in lbFontItems.SelectedItems)
            {
                if (newValues.ContainsKey(item.Field))
                {
                    newValues[item.Field] = tbPreview.Font;
                }
                else
                {
                    newValues.Add(item.Field, tbPreview.Font);
                }
            }
            doReset = false;
        }

        void HSV_Validated(TextBox tb, Predicate<double> validation, string invalidMsg)
        {
            string old = numOldValues[tb];
            numOldValues.Remove(tb);
            if (tb.Text == old) return;
            if (!Calculation.Calculate(tb.Text, out double value))
            {
                MessageBox.Show(string.Format(Resources.Translate("InvalidExpression"), tb.Text), Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                tb.Text = old;
                return;
            }
            if (validation(value))
            {
                refreshRGBValue();
                refreshHexagon();
                pbColorPicker.Invalidate();
                pbTransparency.Invalidate();
                submitColor();
            }
            else
            {
                MessageBox.Show(invalidMsg, Resources.Translate("Exclamation"), MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                tb.Text = old;
            }
        }

        private void tbHex_Validated(object sender, EventArgs e)
        {
            string old = numOldValues[tbHex];
            numOldValues.Remove(tbHex);
            if (tbHex.Text == old) return;
            if (tbHex.TextLength != 8 || !hexValidByte(tbHex.Text.Substring(0, 2)) || 
                !hexValidByte(tbHex.Text.Substring(2, 2)) || 
                !hexValidByte(tbHex.Text.Substring(4, 2)) || 
                !hexValidByte(tbHex.Text.Substring(6, 2)))
            {
                MessageBox.Show(Resources.Translate("InvalidHexColor"), Resources.Translate("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error);
                tbHex.Text = old;
                return;
            }
            submitModify = false;
            nudA.Value = Convert.ToInt32(tbHex.Text.Substring(0, 2), 16);
            nudR.Value = Convert.ToInt32(tbHex.Text.Substring(2, 2), 16);
            nudG.Value = Convert.ToInt32(tbHex.Text.Substring(4, 2), 16);
            nudB.Value = Convert.ToInt32(tbHex.Text.Substring(6, 2), 16);
            refreshHSVPicker();
            submitColor();
            submitModify = true;
        }
        bool hexValidByte(string hex)
        {
            try
            {
                int b = Convert.ToInt32(hex, 16);
                return b >= 0 && b <= 255;
            }
            catch (Exception)
            {
                return false;
            }
        }

        private void SettingsWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            lbColorItems.Items.Clear();
            lbFontItems.Items.Clear();
            lvFonts.Items.Clear();
            lvFontSize.Items.Clear();
        }

        private void tbFont_TextChanged(object sender, EventArgs e)
        {
            syncFont = false;
            foreach (ListViewItem item in lvFonts.SelectedItems)
            {
                item.Selected = false;
            }
            for (int i = lvFonts.Items.Count - 1; i >= 0; i--)
            {
                if (lvFonts.Items[i].Text.IndexOf(tbFont.Text, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    lvFonts.Items[i].Selected = true;
                    break;
                }
            }
            syncFont = true;
        }

        private void tbFontSize_TextChanged(object sender, EventArgs e)
        {
            syncFontSize = false;
            foreach (ListViewItem item in lvFontSize.SelectedItems)
            {
                item.Selected = false;
            }
            for (int i = lvFontSize.Items.Count - 1; i >= 0; i--)
            {
                if (lvFontSize.Items[i].Text.IndexOf(tbFontSize.Text, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    lvFontSize.Items[i].Selected = true;
                    break;
                }
            }
            syncFontSize = true;
        }
        private void nudR_ValueChanged(object sender, EventArgs e)
        {
            if (!submitModify) return;
            setFromPicker = false;
            refreshHSVPicker();
            refreshHexagon();
            submitColor();
        }
        void refreshHSVPicker()
        {
            selectedH = InputBaseColor.GetHue();
            selectedS = InputBaseColor.GetSaturation();
            selectedV = InputBaseColor.GetBrightness();
            tbH.Text = selectedH.ToString();
            tbS.Text = (selectedS * 100).ToString();
            tbV.Text = (selectedV * 100).ToString();
            pbColorPicker.Invalidate();
            pbTransparency.Invalidate();
        }
        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (tabControl1.SelectedIndex)
            {
                case colorIndex:
                    区分大小写ToolStripMenuItem.Checked = colorCS;
                    全字匹配ToolStripMenuItem.Checked = colorMWW;
                    break;
                case fontIndex:
                    区分大小写ToolStripMenuItem.Checked = fontCS;
                    全字匹配ToolStripMenuItem.Checked = fontMWW;
                    break;
                case 2:
                    break;
            }
        }

        private void pbHue_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            pickHue(e);
        }
        void pickHue(System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                setFromPicker = true;
                if (pbHue.ClientRectangle.Contains(e.Location))
                {
                    selectedH = (float)e.X / pbHue.ClientSize.Width * 360;
                }
                else if (e.X < 0)
                {
                    selectedH = 0;
                }
                else if (e.X > pbHue.ClientRectangle.Width)
                {
                    selectedH = 359.9999f;
                }
                pbColorPicker.Invalidate();
                pbTransparency.Invalidate();
                tbH.Text = selectedH.ToString();
                refreshRGBValue();
                refreshHexagon();
                submitColor();
            }
        }

        void refreshRGBValue()
        {
            submitModify = false;
            Color cl = PickedBaseColor;
            nudR.Value = cl.R;
            nudG.Value = cl.G;
            nudB.Value = cl.B;
            submitModify = true;
        }
        void refreshHexagon()
        {
            tbHex.Text = ((int)nudA.Value).ToString("X").PadLeft(2, '0') + ((int)nudR.Value).ToString("X").PadLeft(2, '0') + ((int)nudG.Value).ToString("X").PadLeft(2, '0') + ((int)nudB.Value).ToString("X").PadLeft(2, '0');
        }
        private void 区分大小写ToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
        {
            searchSettingsChanged();
        }

        private void 全字匹配ToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
        {
            searchSettingsChanged();
        }
        void searchSettingsChanged()
        {
            switch (tabControl1.SelectedIndex)
            {
                case colorIndex:
                    searchSettingItem(lbColorItems, tbSearchColor.Text);
                    colorCS = 区分大小写ToolStripMenuItem.Checked;
                    colorMWW = 全字匹配ToolStripMenuItem.Checked;
                    break;
                case fontIndex:
                    searchSettingItem(lbFontItems, tbSearchFont.Text);
                    fontCS = 区分大小写ToolStripMenuItem.Checked;
                    fontMWW = 全字匹配ToolStripMenuItem.Checked;
                    break;
            }
        }
    }
}
